package com.heiye.count.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.heiye.count.api.dto.FindCommentCountByIdRspDTO;
import com.heiye.count.api.dto.FindCommentCountByIdsReqDTO;
import com.heiye.count.biz.constants.RedisKeyConstants;
import com.heiye.count.biz.domain.dataobject.CommentCountDO;
import com.heiye.count.biz.domain.mapper.CommentCountDOMapper;
import com.heiye.count.biz.service.CommentCountService;
import com.heiye.framework.common.response.Response;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.K;
import org.heiye.framework.biz.context.thread.LoginUserContextHolder;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: heiye
 * @date: 2025/08/07 上午11:04
 * @version: v1.0.0
 * @description: 评论计数业务
 */
@Slf4j
@Service
public class CommentCountServiceImpl implements CommentCountService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private CommentCountDOMapper commentCountDOMapper;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 查询评论计数数据
     *
     * @param findCommentCountByIdsReqDTO
     * @return
     */
    @Override
    public Response<List<FindCommentCountByIdRspDTO>> findCommentsCountData(FindCommentCountByIdsReqDTO findCommentCountByIdsReqDTO) {
        // 评论 ID 集合
        List<Long> commentIds = findCommentCountByIdsReqDTO.getCommentIds();

        // 构建评论计数 Redis Key 集合
        List<String> commentCountRedisKeys = commentIds.stream()
                .map(RedisKeyConstants::buildCountCommentKey)
                .toList();

        // pipelined 批量查询评论计数数据
        List<Object> redisPipelineResult = redisTemplate.executePipelined(new SessionCallback<>() {
            @Override
            public Object execute(RedisOperations operations) {
                commentCountRedisKeys.forEach(key -> operations.opsForHash().get(key, RedisKeyConstants.FIELD_LIKE_TOTAL));
                return null;
            }
        });

        // 构建返回参数
        List<FindCommentCountByIdRspDTO> findCommentCountByIdRspDTOS = Lists.newArrayList();
        // 需要查询计数数据的评论 ID 集合
        List<Long> needQueryCommentIds = Lists.newArrayList();

        // redis 中存在评论计数数据
        if (CollUtil.isNotEmpty(redisPipelineResult)) {

            for (int i = 0; i < commentIds.size(); i++) {
                Integer likeTotal = (Integer) redisPipelineResult.get(i);
                // 评论计数数据不存在 Redis 中
                if (Objects.isNull(likeTotal)) {

                    // 添加到需要查询的评论 ID 集合中
                    needQueryCommentIds.add(commentIds.get(i));
                }

                // 构建评论计数数据
                FindCommentCountByIdRspDTO findCommentCountByIdRspDTO = FindCommentCountByIdRspDTO.builder()
                        .commentId(commentIds.get(i))
                        .likeTotal(Objects.nonNull(likeTotal) ? Long.valueOf(likeTotal) : null)
                        .build();

                findCommentCountByIdRspDTOS.add(findCommentCountByIdRspDTO);
            }

            // 评论计数全部数据都存在于 Redis 中
            if (CollUtil.isEmpty(needQueryCommentIds)) {
                // 直接返回结果
                return Response.success(findCommentCountByIdRspDTOS);
            }
        }

        // 查询数据库
        List<CommentCountDO> commentCountDOS = commentCountDOMapper.selectByCommentIds(needQueryCommentIds);

        if (CollUtil.isNotEmpty(commentCountDOS)) {
            // 评论 ID 与 点总数 Map
            Map<Long, Long> commentIdAndLikeTotalMap = commentCountDOS.stream().collect(Collectors.toMap(
                    CommentCountDO::getCommentId,
                    CommentCountDO::getLikeTotal
            ));

            // 异步将评论计数数据，同步到 Redis 中
            syncCommentHashCount2Redis(findCommentCountByIdRspDTOS, commentIdAndLikeTotalMap);

            // 构建返回参数
            for (FindCommentCountByIdRspDTO findCommentCountByIdRspDTO : findCommentCountByIdRspDTOS) {
                Long likeTotal = findCommentCountByIdRspDTO.getLikeTotal();
                // 获取评论 ID 对应的计数数据
                Long mapLikeTotal = commentIdAndLikeTotalMap.get(findCommentCountByIdRspDTO.getCommentId());

                if (Objects.isNull(likeTotal)) {
                    findCommentCountByIdRspDTO.setLikeTotal(Objects.nonNull(mapLikeTotal) ? mapLikeTotal : 0);
                }
            }
        }

        return Response.success(findCommentCountByIdRspDTOS);
    }


    /**
     * 同步评论计数数据到 Redis 中
     *
     * @param findCommentCountByIdRspDTOS
     * @param commentIdAndLikeTotalMap
     */
    private void syncCommentHashCount2Redis(List<FindCommentCountByIdRspDTO> findCommentCountByIdRspDTOS, Map<Long, Long> commentIdAndLikeTotalMap) {
        redisTemplate.executePipelined(new SessionCallback<>() {
            @Override
            public Object execute(RedisOperations operations) {
                for (FindCommentCountByIdRspDTO findCommentCountByIdRspDTO : findCommentCountByIdRspDTOS) {
                    // 评论 ID
                    Long commentId = findCommentCountByIdRspDTO.getCommentId();
                    // 点赞总数
                    Long likeTotal = findCommentCountByIdRspDTO.getLikeTotal();

                    // 如果点赞总数不为空，说明评论计数数据已经存在 Redis 中
                    if (Objects.nonNull(likeTotal)) {
                        continue;
                    }

                    // 评论计数数据
                    Long mapLikeTotal = commentIdAndLikeTotalMap.get(commentId);

                    // 构建评论计数 Redis Key
                    String countCommentKey = RedisKeyConstants.buildCountCommentKey(commentId);

                    // 设置 Field 计数
                    Map<String, Long> countMap = Maps.newHashMap();

                    countMap.put(RedisKeyConstants.FIELD_LIKE_TOTAL, Objects.nonNull(mapLikeTotal) ? mapLikeTotal : 0);

                    // 批量添加 Hash 的计数 Field
                    operations.opsForHash().putAll(countCommentKey, countMap);

                    // 设置随机过期时间 (1 小时以内)
                    long expireTime = 60 * 30 + RandomUtil.randomInt(60 * 30);
                    operations.expire(countCommentKey, expireTime, TimeUnit.SECONDS);
                }
                return null;
            }
        });
    }
}
